/**
 * FileUploader
 *
 * @version 1.0.0
 */
(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
    typeof define === 'function' && define.amd ? define(['exports'], factory) :
    (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.FileUploader = {}));
})(this, (function (exports) { 'use strict';

    var FileUploader = /** @class */ (function () {
        function FileUploader(options) {
            this.xhr = null;
            this.doc = document;
            this.filesQueue = null;
            this.fileQueuedHandler = null;
            this.fileQueuedErrorHandler = null;
            this.filesQueuedCompleteHandler = null;
            this.uploadProgressHandler = null;
            this.uploadSuccessHandler = null;
            this.uploadCompleteHandler = null;
            this.isUploading = false;
            this.configs = {
                fileElement: null,
                postParams: null,
                headers: null,
                server: '',
                fileName: 'file',
                useFormData: true,
                withCredentials: false,
                auto: false,
                multiple: false,
                accept: 'image/jpg, image/jpeg, image/png, image/gif',
                // 5 Mb
                fileSizeLimit: 1024 * 1024 * 5
            };
            this.initOptions(options);
            this.initFileComponent();
        }
        FileUploader.prototype.setupXHR = function (file) {
            var _this = this;
            this.xhr = new XMLHttpRequest();
            this.xhr.ontimeout = this.xhr.onabort = this.xhr.onerror = function (e) {
                _this.isUploading = false;
            };
            this.xhr.upload.onprogress = function (e) {
                var percent = 0;
                if (e.lengthComputable) {
                    percent = e.loaded / e.total;
                }
                if (null !== _this.uploadProgressHandler) {
                    _this.uploadProgressHandler(file, percent);
                }
            };
            this.xhr.onreadystatechange = function () {
                if (4 !== _this.xhr.readyState) {
                    return;
                }
                _this.xhr.upload.onprogress = null;
                _this.xhr.onreadystatechange = null;
                if (200 === _this.xhr.status) {
                    if (null !== _this.uploadSuccessHandler) {
                        _this.uploadSuccessHandler(file, _this.xhr.responseText);
                    }
                }
                _this.isUploading = false;
                // Next
                _this.startUpload();
            };
            this.xhr.open('POST', this.configs.server, true);
            if (this.configs.withCredentials) {
                this.xhr.withCredentials = true;
            }
            // headers
            if (null !== this.configs.headers) {
                for (var k in this.configs.headers) {
                    this.xhr.setRequestHeader(k, this.configs.headers[k]);
                }
            }
        };
        FileUploader.prototype.clearFileInput = function () {
            if (this.configs.fileElement) {
                this.configs.fileElement.value = '';
            }
        };
        FileUploader.prototype.initOptions = function (options) {
            var _this = this;
            for (var k in options) {
                this.configs[k] = options[k];
            }
            // 自动上传
            if (this.configs.auto) {
                this.filesQueuedCompleteHandler = function () {
                    _this.startUpload();
                };
            }
        };
        FileUploader.prototype.initFileComponent = function () {
            var _this = this;
            if (null === this.configs.fileElement) {
                return;
            }
            this.configs.fileElement.setAttribute('accept', this.configs.accept);
            if (this.configs.multiple) {
                this.configs.fileElement.setAttribute('multiple', 'multiple');
            }
            this.configs.fileElement.onchange = function (e) {
                _this.selectFiles(e.target.files);
            };
        };
        FileUploader.prototype.isValidFile = function (file) {
            if (file.size > this.configs.fileSizeLimit) {
                return {
                    valid: false,
                    type: FileUploader.ERROR_FILE_SIZE_LIMIT,
                    message: 'File is too big'
                };
            }
            if (-1 === this.configs.accept.indexOf(file.extension)) {
                return {
                    valid: false,
                    type: FileUploader.ERROR_INVALID_FILE_TYPE,
                    message: 'File type is invalid'
                };
            }
            return {
                valid: true,
                type: 0,
                message: ''
            };
        };
        /**
         * Add files to queue
         */
        FileUploader.prototype.selectFiles = function (fileList) {
            if (fileList.length <= 0) {
                return;
            }
            if (null === this.filesQueue) {
                this.filesQueue = new Queue();
            }
            var len = fileList.length;
            var tmpFile;
            for (var i = 0; i < len; i++) {
                tmpFile = new UploadFile(fileList[i]);
                // Check file
                var validate = this.isValidFile(tmpFile);
                if (!validate.valid) {
                    if (null !== this.fileQueuedErrorHandler) {
                        this.fileQueuedErrorHandler(tmpFile, validate.type);
                    }
                    continue;
                }
                this.filesQueue.add(tmpFile);
                if (null !== this.fileQueuedHandler) {
                    this.fileQueuedHandler(tmpFile);
                }
            }
            if (null !== this.filesQueuedCompleteHandler) {
                this.filesQueuedCompleteHandler({
                    selected: len,
                    queued: this.filesQueue.size,
                    files: this.filesQueue.toArray()
                });
            }
        };
        FileUploader.prototype.uploadAsFormData = function (file) {
            var fd = new FormData();
            if (null !== this.configs.postParams) {
                for (var k in this.configs.postParams) {
                    fd.append(k, this.configs.postParams[k]);
                }
            }
            fd.append(this.configs.fileName, file.nativeFile);
            this.setupXHR(file);
            this.xhr.send(fd);
        };
        FileUploader.prototype.uploadAsBinary = function (file) {
            var _this = this;
            var fr = new FileReader();
            fr.onload = function () {
                _this.xhr.send(fr.result);
                fr.onload = null;
                fr = null;
            };
            this.setupXHR(file);
            this.xhr.overrideMimeType('application/octet-stream');
            fr.readAsArrayBuffer(file.nativeFile);
        };
        /**
         * Set post parameters
         */
        FileUploader.prototype.setPostParams = function (paramsObject) {
            this.configs.postParams = paramsObject;
        };
        /**
         * Append or replace a post parameter
         */
        FileUploader.prototype.addPostParam = function (name, value) {
            if (null === this.configs.postParams) {
                this.configs.postParams = {};
            }
            this.configs.postParams[name] = value;
        };
        /**
         * Start upload
         */
        FileUploader.prototype.startUpload = function () {
            if (this.isUploading) {
                return;
            }
            if (null === this.filesQueue) {
                return;
            }
            var file = this.filesQueue.take();
            if (null === file) {
                this.clearFileInput();
                if (null !== this.uploadCompleteHandler) {
                    this.uploadCompleteHandler();
                }
                return;
            }
            this.isUploading = true;
            if (this.configs.useFormData) {
                this.uploadAsFormData(file);
                return;
            }
            this.uploadAsBinary(file);
        };
        FileUploader.ERROR_FILE_SIZE_LIMIT = 1;
        FileUploader.ERROR_INVALID_FILE_TYPE = 2;
        return FileUploader;
    }());
    var Queue = /** @class */ (function () {
        function Queue() {
            this.headNode = null;
            this.tailNode = null;
            this.size = 0;
        }
        Queue.prototype.add = function (data) {
            var node = new QueueNode(data, null);
            this.tailNode = node;
            if (0 === this.size) {
                this.headNode = node;
            }
            else {
                this.tailNode.next = node;
            }
            this.size++;
        };
        Queue.prototype.take = function () {
            if (0 === this.size || null === this.headNode) {
                return null;
            }
            var data = this.headNode.data;
            var tmpHeadNode = this.headNode;
            // 从队列去除头节点
            this.headNode = tmpHeadNode.next;
            tmpHeadNode.next = null;
            tmpHeadNode = null;
            // 没节点了
            if (null === this.headNode) {
                this.headNode = this.tailNode = null;
            }
            this.size--;
            return data;
        };
        Queue.prototype.delete = function (data) {
            var prev = null;
            var current = null;
            for (current = this.headNode; null !== current; prev = current, current = current.next) {
                if (data === current.data) {
                    // 前一个节点绕过本节点
                    if (null !== prev) {
                        prev.next = current.next;
                    }
                    // 删除头节点
                    if (current === this.headNode) {
                        this.headNode = current.next;
                    }
                    // 删除尾节点
                    if (current === this.tailNode) {
                        this.tailNode = prev;
                    }
                    current.next = null;
                    this.size -= 1;
                    break;
                }
            }
        };
        Queue.prototype.clear = function () {
            while (null !== this.take()) { }
        };
        Queue.prototype.toArray = function () {
            var ret = [];
            var current = null;
            for (current = this.headNode; null !== current; current = current.next) {
                ret.push(current.data);
            }
            return ret;
        };
        return Queue;
    }());
    var QueueNode = /** @class */ (function () {
        function QueueNode(data, next) {
            this.data = data;
            this.next = next;
        }
        return QueueNode;
    }());
    var UploadFile = /** @class */ (function () {
        function UploadFile(file) {
            this.nativeFile = file;
            this.id = 'xef' + UploadFile.uuid++;
            this.name = file.name;
            this.size = file.size;
            this.type = file.type;
            this.extension = file.type.substring(file.type.indexOf('/') + 1);
            this.lastModified = file.lastModified;
        }
        UploadFile.uuid = 0;
        return UploadFile;
    }());

    exports.Queue = Queue;
    exports.QueueNode = QueueNode;
    exports.UploadFile = UploadFile;
    exports.default = FileUploader;

    Object.defineProperty(exports, '__esModule', { value: true });

}));
